#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

//默认的线程数量
const int g_thread_num = 3;

// 线程池的本质是生产消费模型
template <class T>
class ThreadPool
{
//公开一些方法，让routine回调函数可以访问到线程池内部的私有成员变量，且这些任务在使用的时候是被加过锁的
public:
    //返回锁
    pthread_mutex_t *getMutex()
    {
        return &lock;
    }
    //判断任务队列是否为空
    bool isEmpty()
    {
        return task_queue_.empty();
    }
    //在条件变量下等待，并释放锁，等待结束，自动持有锁
    void waitCond()
    {
        pthread_cond_wait(&cond, &lock);
    }
    //返回任务对象
    T getTask()
    {
        T t = task_queue_.front();
        task_queue_.pop();
        return t;
    }

private:
    //线程池的构造
    //单例，所以构造私有
    ThreadPool(int thread_num = g_thread_num) 
            : num_(thread_num)
    {
        //对锁进行初始化
        pthread_mutex_init(&lock, nullptr);
        //对条件变量进行初始化
        pthread_cond_init(&cond, nullptr);

        //将线程对象pushback到线程池
        for (int i = 1; i <= num_; i++)
        {
            //将this指针传入，拿到当前线程池对象，从而调用线程池public的方法，获取task队列
            threads_.push_back(new Thread(i, routine, this));       
        }
    }

    //禁止拷贝构造
    ThreadPool(const ThreadPool<T> &other) = delete;
    //禁止赋值运算符重载
    const ThreadPool<T> &operator=(const ThreadPool<T> &other) = delete;

public:
    // 懒汉模式单例
    static ThreadPool<T> *getThreadPool(int num = g_thread_num)
    {
        // if (nullptr == thread_ptr) 可以有效减少未来必定要进行加锁检测的问题
        // 拦截 大量的在已经创建好单例的时候，剩余线程请求单例的而直接访问锁的行为
        if (nullptr == thread_ptr) 
        {
            //单例本身也会被多线程申请使用，所以getThreadPool不是线程安全的，要加锁
            lockGuard lockguard(&mutex);
            // 未来任何一个线程想获取单例，都必须调用getThreadPool接口，一定会存在大量的申请和释放锁的行为，这个是无用且浪费资源的
            if (nullptr == thread_ptr)
            {
                thread_ptr = new ThreadPool<T>(num);
            }
        }
        return thread_ptr;
    }

    //线程启动
    void run()
    {
        //启动所有线程
        for (auto &iter : threads_)
        {
            iter->start();
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "启动成功");
        }
    }

    // 消费过程，线程执行的方法
    // static避免了类内成员函数传入this指针
    static void *routine(void *args)
    {
        //拿到传入到回调函数的结构体
        ThreadData *td = (ThreadData *)args;
        //拿到this指针指向的线程池对象
        ThreadPool<T> *tp = (ThreadPool<T> *)td->args_;

        while (true)
        {
            T task;

            {
                lockGuard lockguard(tp->getMutex());
                while (tp->isEmpty())
                {
                    tp->waitCond();
                }
                // 读取任务
                task = tp->getTask(); // 将任务从共享空间拿到自己的私有空间
            }
            //处理任务，task类支持仿函数的operator()重载
            task(td->name_);
        }
    }

    //任务push到任务队列，生产过程
    void pushTask(const T &task)
    {
        //加锁
        lockGuard lockguard(&lock);
        //push任务
        task_queue_.push(task);
        pthread_cond_signal(&cond);
    }

    //线程池的析构
    ~ThreadPool()
    {
        //遍历所有线程，对其进行join并delete
        for (auto &iter : threads_)
        {
            iter->join();
            delete iter;
        }
        //解锁
        pthread_mutex_destroy(&lock);
        //释放条件变量
        pthread_cond_destroy(&cond);
    }

private:
    //声明一批线程
    std::vector<Thread *> threads_;
    //线程的数量
    int num_;
    //表示任务队列
    std::queue<T> task_queue_;

    //静态的线程池指针，支持单例模式
    static ThreadPool<T> *thread_ptr; 
    //考虑多线程使用单例的过程，声明锁保护getThreadPool
    static pthread_mutex_t mutex;

    
    //声明锁
    pthread_mutex_t lock;
    //声明条件变量
    pthread_cond_t cond;
};

//对静态的线程池指针进行初始化
template <typename T>
ThreadPool<T> *ThreadPool<T>::thread_ptr = nullptr;

//对静态的锁进行初始化
template <typename T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;